#!/usr/bin/env python
# coding: utf-8

# # 图像数据加载与增强
# 在计算机视觉任务中，数据量过小、样本场景单一等问题都会影响模型的训练效果，用户可以通过数据增强操作对图像进行预处理，从而提升模型的泛化性。luojianet提供了`c_transforms`模块和`py_transforms`模块供用户进行多种数据增强操作，二者的区别如下，用户也可以自定义函数或者算子进行数据增强。
# 
# - `c_transforms`：基于C++的OpenCV实现，提供了多种图像增强功能，具有较高的性能；
# 
# - `py_transforms`：基于Python的PIL实现，提供了多种图像增强功能，并提供了PIL Image和NumPy数组之间的传输方法。
# 
# 下面将以CIFAR-10数据集和MNIST数据集为例，简要介绍这两种图像数据加载的方式和几种常用的`c_transforms`模块和`py_transforms`模块数据增强算子的使用方法，更多图像类型的数据集加载方式可参考[API文档](https://www.luojianet.cn/docs/zh-CN/r1.7/api_python/luojianet.dataset.html#图)，更多图像数据增强算子相关信息可参考[API文档](http://58.48.42.237/luojiaNet/luojiaNetapi/)。
# 
# ## 加载图像数据
# 
# 1中低阶API实现深度学习. 以下示例代码分别将CIFAR-10数据集和MNIST数据集下载并解压到指定位置，网络状况良好的情况下此段代码预计需执行三至五分钟。

# In[1中低阶API实现深度学习]:

# 手动下载CIFAR-10数据集并解压
#"https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/datasets/cifar-10-binary.tar.gz"

# 解压后数据集文件的目录结构如下：

# datasets/
# └── cifar-10-batches-bin
#     ├── batches.meta.txt
#     ├── data_batch_1.bin
#     ├── data_batch_2.bin
#     ├── data_batch_3.bin
#     ├── data_batch_4.bin
#     ├── data_batch_5.bin
#     ├── readme.html
#     └── test_batch.bin
# ```
#同理，手动下载解压MNIST数据集，保存路径为 ./mnist
#MNIST_labels：http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz
#MNIST_images: http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz


# 2高级数据集管理. 使用[luojianet.dataset.Cifar10Dataset]接口加载MNIST数据集。示例代码如下：

# In[2高级数据集管理]:


import matplotlib.pyplot as plt
import luojianet.dataset as ds
# get_ipython().run_line_magic('matplotlib', 'inline')

DATA_DIR_MNIST = "./mnist/"
DATA_DIR_CIFAR10 = "./datasets/cifar-10-batches-bin/"

# 加载数据集，选取4张图片
dataset_cifar10 = ds.Cifar10Dataset(DATA_DIR_CIFAR10, num_samples=4)
dataset_mnist = ds.MnistDataset(DATA_DIR_MNIST, num_samples=4)

def printDataset(dataset_list, name_list):
    """显示数据集"""
    dataset_sizes = []
    for dataset in dataset_list:
        dataset_sizes.append(dataset.get_dataset_size())
    row = len(dataset_list)      # 画布行数
    column = max(dataset_sizes)  # 画布列数
    pos = 1
    for i in range(row):
        for data in dataset_list[i].create_dict_iterator(output_numpy=True):
            plt.figure()
            plt.subplot(row, column, pos)                          # 显示位置
            plt.imshow(data['image'].squeeze(), cmap=plt.cm.gray)  # 显示内容
            plt.title(data['label'])                               # 显示标题
            print(name_list[i], " shape:", data['image'].shape, "label:", data['label'])
            pos = pos + 1
            plt.close()
        pos = column * (i + 1) + 1

printDataset([dataset_cifar10, dataset_mnist], ["CIFAR-10", "MNIST"])



# ## c_transforms模块
# 
# `c_transforms`是基于C++的OpenCV实现，提供了多种图像增强功能，具有较高的性能。
# 
# ### RandomCrop
# 
# `RandomCrop`操作对输入图像进行在随机位置的裁剪。
# 
# 参数说明：
# 
# - `size`：裁剪图像的尺寸。
# 
# - `padding`：填充的像素数量。
# 
# - `pad_if_needed`：原图小于裁剪尺寸时，是否需要填充。
# 
# - `fill_value`：在常量填充模式时使用的填充值。
# 
# - `padding_mode`：填充模式。
# 
# 下面的样例首先使用顺序采样器加载CIFAR-10数据集，然后对已加载的图片进行长宽均为10的随机裁剪，最后输出裁剪前后的图片形状及对应标签，并对图片进行了展示。

# In[3图像处理]:


import matplotlib.pyplot as plt
import luojianet.dataset as ds
import luojianet.dataset.vision.c_transforms as c_trans

ds.config.set_seed(1)

# CIFAR-10数据集加载路径
DATA_DIR = "./datasets/cifar-10-batches-bin/"

# 使用SequentialSampler采样器选取3张图片
sampler = ds.SequentialSampler(num_samples=3)
dataset1 = ds.Cifar10Dataset(DATA_DIR, sampler=sampler)

# 使用RandomCrop对原图进行10*10随机裁剪操作
random_crop = c_trans.RandomCrop([10, 10])
dataset2 = dataset1.map(operations=random_crop, input_columns=["image"])

printDataset([dataset1, dataset2], ["Source image", "Cropped image"])


# 从上面的打印和图片显示结果可以看出，图片随机裁剪前后，标签不变，形状发生了变化。裁剪前的图片分辨率为32×32，裁剪后为10×10。
# 
# ### RandomHorizontalFlip
# 
# `RandomHorizontalFlip`操作对输入图像进行随机水平翻转。
# 
# 参数说明：
# 
# - `prob`: 单张图片发生翻转的概率。
# 
# 下面的样例首先使用随机采样器加载CIFAR-10数据集，然后对已加载的图片进行概率为0.8的随机水平翻转，最后输出翻转前后的图片形状及对应标签，并对图片进行了展示。

# In[4自然语言]:


import matplotlib.pyplot as plt
import luojianet.dataset as ds
import luojianet.dataset.vision.c_transforms as c_trans

ds.config.set_seed(1)

# CIFAR-10数据集加载路径
DATA_DIR = "./datasets/cifar-10-batches-bin/"

# 使用RandomSampler采样器随机选取4张图片
sampler = ds.RandomSampler(num_samples=4)
dataset1 = ds.Cifar10Dataset(DATA_DIR, sampler=sampler)

# 使用RandomHorizontalFlip对原图进行随机水平翻转
random_horizontal_flip = c_trans.RandomHorizontalFlip(prob=0.8)
dataset2 = dataset1.map(operations=random_horizontal_flip, input_columns=["image"])

printDataset([dataset1, dataset2], ["Source image", "Flipped image"])


# 从上面的打印和图片显示结果可以看出，经过随机水平翻转操作后，图像的形状、标签均未发生变化，部分图片被水平翻转。
# 
# ### Resize
# 
# `Resize`操作对输入图像进行缩放。
# 
# 参数说明：
# 
# - `size`：缩放的目标大小。
# - `interpolation`：缩放时采用的插值方式。
# 
# 下面的样例首先加载[MNIST数据集[2高级数据集管理]](#参考文献)，然后将已加载的图片缩放至(101, 101)大小，最后输出缩放前后的图片形状及对应标签，并对图片进行了展示。

# In[5]:


import matplotlib.pyplot as plt
import luojianet.dataset as ds
import luojianet.dataset.vision.c_transforms as c_trans

# MNIST数据集加载路径
DATA_DIR = "./mnist/"

# 加载MNIST数据集，选取4张图片
dataset1 = ds.MnistDataset(DATA_DIR, num_samples=4, shuffle=False)

# 使用Resize操作对图像进行101×101缩放
resize = c_trans.Resize(size=[101, 101])
dataset2 = dataset1.map(operations=resize, input_columns=["image"])

printDataset([dataset1, dataset2], ["Source image", "Resized image"])


# 从上面的打印和图片显示结果可以看出，缩放前后，图片的形状发生了变化，标签未变。缩放前图片分辨率为28×28，缩放后，图片分辨率为101×101。
# 
# ### Invert
# 
# `Invert`操作对输入图像进行反相处理。
# 
# 下面的样例首先加载CIFAR-10数据集，然后定义反相操作并作用于已加载的图片，最后输出反相前后的图片形状和标签，并对图片进行展示。

# In[6]:


import matplotlib.pyplot as plt
import luojianet.dataset as ds
import luojianet.dataset.vision.c_transforms as c_trans

ds.config.set_seed(18)

# CIFAR-10数据集加载路径
DATA_DIR = "./datasets/cifar-10-batches-bin/"

# 加载CIFAR-10数据集，选取4张图片
dataset1 = ds.Cifar10Dataset(DATA_DIR, num_samples=4, shuffle=True)

# 对图片进行反相操作
invert = c_trans.Invert()
dataset2 = dataset1.map(operations=invert, input_columns=["image"])

printDataset([dataset1, dataset2], ["Source image", "Inverted image"])


# 从上面的打印和图片显示结果可以看出，反相操作前后，图片的形状和标签未变，颜色发生了变化。
# 
# ## py_transforms模块
# 
# ### Compose
# 
# `Compose`操作接收一个`transforms`列表，将列表中的数据增强操作依次作用于数据集图片。
# 
# 下面的样例首先加载[CIFAR-10数据集[1中低阶API实现深度学习]](#参考文献)，然后同时定义解码、缩放和数据类型转换操作，并作用于已加载的图片，最后输出处理后的图片形状及对应标签，并对图片进行展示。

# In[7]:


import matplotlib.pyplot as plt
import luojianet.dataset as ds
import luojianet.dataset.vision.py_transforms as py_trans
from luojianet.dataset.transforms.py_transforms import Compose
from PIL import Image
# get_ipython().run_line_magic('matplotlib', 'inline')

ds.config.set_seed(8)

# CIFAR-10数据集加载路径
DATA_DIR = "./datasets/cifar-10-batches-bin/"

# 加载CIFAR-10数据集，选取5张图片
dataset1 = ds.Cifar10Dataset(DATA_DIR, num_samples=5, shuffle=True)

def decode(image):
    """定义解码函数"""
    return Image.fromarray(image)

# 定义transforms列表
transforms_list = [
    decode,
    py_trans.Resize(size=(200, 200)),
    py_trans.ToTensor()
]

# 通过Compose操作将transforms列表中函数作用于数据集图片
compose_trans = Compose(transforms_list)
dataset2 = dataset1.map(operations=compose_trans, input_columns=["image"])

# 打印数据增强操作后图片的形状、标签
image_list, label_list = [], []
for data in dataset2.create_dict_iterator():
    image_list.append(data['image'])
    label_list.append(data['label'])
    print("Transformed image Shape:", data['image'].shape, ", Transformed label:", data['label'])

num_samples = len(image_list)
for i in range(num_samples):
    plt.figure()
    plt.subplot(1, len(image_list), i + 1)
    plt.imshow(image_list[i].asnumpy().transpose(1, 2, 0))
    plt.title(label_list[i].asnumpy())


# 从上面的打印和图片显示结果可以看出，经过transforms列表中的数据增强操作后，图片标签未变，形状发生了变化，分辨率缩放为200×200。
# 
# ## 注意事项
# 
# 在数据管道处理模式中，请谨慎混用`c_transforms`与`py_transforms`，因为两者在数据的计算管道（即Pipeline）中运行的方式存在差异。
# 
# 混用会引发C++与Python切换的成本，从而降低处理性能，因此建议尽量不要过度混用两个模块的算子。
# 
# > 注：Eager模式混用`c_transforms`与`py_transforms`不受运行方式差异影响
# 
# ### 推荐的使用方式
# 
# 1中低阶API实现深度学习. 优先推荐单独使用`py_transform`或`c_transform`。如下图所示，不存在Python层和C++层切换使用的情况。
# 
#     ![tranform-c-py1](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r1.7/tutorials/source_zh_cn/advanced/dataset/images/transform_recommended_1.png)
# 
# 2高级数据集管理. 先使用`py_transform`，再使用`c_transform`。如下图所示，先完成Python层的操作后，再完成C++层的操作。
# 
#     ![tranform-c-py2](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r1.7/tutorials/source_zh_cn/advanced/dataset/images/transform_recommended_2.png)
# 
# 3图像处理. 先使用`c_transform`，再使用`py_transform`。如下图所示，先完成C++层的操作后，再完成Python层的操作。
# 
#     ![tranform-c-py3](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r1.7/tutorials/source_zh_cn/advanced/dataset/images/transform_recommended_3.png)
# 
# ### 不推荐的使用方式
# 
# 在两种transform之间频繁切换。如下图所示，这种在Python层和C++层来回切换的使用方式是极不推荐的。
# 
# ![tranform-c-py4](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r1.7/tutorials/source_zh_cn/advanced/dataset/images/transform_not_recommended.png)
# 
# ## 参考文献
# 
# [1中低阶API实现深度学习] Alex Krizhevsky. [Learning_Multiple Layers of Features from Tiny Images](http://www.cs.toronto.edu/~kriz/learning-features-2009-TR.pdf).
# 
# [2高级数据集管理] Y. LeCun, L. Bottou, Y. Bengio, and P. Haffner. [Gradient-based learning applied to document recognition](http://yann.lecun.com/exdb/publis/pdf/lecun-98.pdf).

# > 本章节中的示例代码依赖第三方支持包`matplotlib`，可使用命令`pip install matplotlib`安装。如本文档以Notebook运行时，完成安装后需要重启kernel才能执行后续代码。
